/*
 * Voice.java
 *
 * Created: Sat Mar  13 14:41:11 2010
 *
 * Copyright (C) 2010 Techventus, LLC
 * 
 * Techventus, LLC is not responsible for any use or misuse of this product.
 * In using this software you agree to hold harmless Techventus, LLC and any other
 * contributors to this project from any damages or liabilities which might result 
 * from its use.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package sharedServlets;

import gvjava.org.json.JSONException;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;



/**
 * The Class Voice. This class is the basis of the entire API and contains all
 * the components necessary to connect and authenticate with Google Voice, place
 * calls and SMS, and pull in the raw data from the account.
 * 
 * @author Techventus, LLC
 */
@SuppressWarnings("deprecation")
public class Voice {

        public boolean PRINT_TO_CONSOLE;
        /** 
         * keeps the list of phones - lazy
        */

        String general = null;
        String phonesInfo = null;
        String rnrSEE = null;

        /**
         * Short string identifying your application, for logging purposes. This string should take the form:
         * "companyName-applicationName-versionID". See: http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Request
         */
        String source = null;
        /**
         * User's full email address. It must include the domain (i.e. johndoe@gmail.com).
         */
        private String user = null;
        /**
         * User's password.
         */
        private String pass = null;
        /**
   * Google Voice Phone Number.
   */
  private String phoneNumber = null;
        /**
         * Once the login information has been successfully authenticated, Google returns a token, which your 
         * application will reference each time it requests access to the user's account.
         * This token must be included in all subsequent requests to the Google service for this account. 
         * Authorization tokens should be closely guarded and should not be given to any other application, 
         * as they represent access to the user's account. The time limit on the token varies depending on 
         * which service issued it.
         */
        private String authToken = null;
        /**
         * (optional) Token representing the specific CAPTCHA challenge. Google supplies this token and the 
         * CAPTCHA image URL in a login failed response with the error code "CaptchaRequired".
         */
        private String captchaToken = null;
        /**
         * Url of the image with the captcha - only filled after a captacha response to a login try
         */
        private String captchaUrl = null;
        private String captchaUrl2 = null;
        /** Counts the amount of redirects we are doing in the get(String url) method to avoid infinite loop */
        private int redirectCounter = 0;
        /** Maximum amount of redirects before we throw an exception */
        private static int MAX_REDIRECTS = 5;
        final static String enc = "UTF-8";
        final static String USER_AGENT = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.A.B.C Safari/525.13";
        public final static String GOOGLE = "GOOGLE";
        public final static String HOSTED = "HOSTED";
        public final static String HOSTED_OR_GOOGLE = "HOSTED_OR_GOOGLE";
        /**
         * Type of account to request authorization for. Possible values are: <br/><br/>
         * -<b>GOOGLE</b> (get authorization for a Google account only) <br/>
         * -<b>HOSTED</b> (get authorization for a hosted account only) <br/>
         * -<b>HOSTED_OR_GOOGLE</b> (get authorization first for a hosted account; if attempt fails, get 
         * authorization for a Google account)<br/><br/>                
         * Use <b>HOSTED_OR_GOOGLE</b> if you're not sure which type of account you want authorization for. 
         * If the user information matches both a hosted and a Google account, only the hosted account is authorized.
         */
        private String account_type = GOOGLE; 
        
        /**
         * Name of the Google service you're requesting authorization for. Each service using the Authorization 
         * service is assigned a name value; for example, the name associated with Google Calendar is 'cl'. 
         * This parameter is required when accessing services based on Google Data APIs. For specific service 
         * names, refer to the service documentation.
         */
        final static String SERVICE = "grandcentral";
        final static String generalURLString = "https://www.google.com/voice/";
        final static String loginURLString = "https://www.google.com/accounts/ClientLogin";
        final static String inboxURLString = "https://www.google.com/voice/inbox/recent/inbox/";
        final static String starredURLString = "https://www.google.com/voice/inbox/recent/starred/";
        final static String recentAllURLString = "https://www.google.com/voice/inbox/recent/all/";
        final static String spamURLString = "https://www.google.com/voice/inbox/recent/spam/";
        final static String trashURLString = "https://www.google.com/voice/inbox/recent/spam/";
        final static String voicemailURLString = "https://www.google.com/voice/inbox/recent/voicemail/";
        final static String smsURLString = "https://www.google.com/voice/inbox/recent/sms/";
        final static String recordedURLString = "https://www.google.com/voice/inbox/recent/recorded/";
        final static String placedURLString = "https://www.google.com/voice/inbox/recent/placed/";
        final static String receivedURLString = "https://www.google.com/voice/inbox/recent/received/";
        final static String missedURLString = "https://www.google.com/voice/inbox/recent/missed/";
        final static String phoneEnableURLString = "https://www.google.com/voice/settings/editDefaultForwarding/";
        final static String generalSettingsURLString = "https://www.google.com/voice/settings/editGeneralSettings/";
        final static String phonesInfoURLString = "https://www.google.com/voice/settings/tab/phones";
        final static String groupsInfoURLString = "https://www.google.com/voice/settings/tab/groups";
        final static String voicemailInfoURLString = "https://www.google.com/voice/settings/tab/voicemailsettings";
        final static String groupsSettingsURLString = "https://www.google.com/voice/settings/editGroup/";
  final static String voicemailDownloadURLString = "https://www.google.com/voice/media/send_voicemail/";

        /**
         * Instantiates a new voice. This constructor is deprecated. Try
         * Voice(String user, String pass) which automatically determines rnrSee and
         * assigns a source.
         * 
         * @param user
         *            the user
         * @param pass
         *            the pass
         * @param source
         *            the source
         * @param rnrSee
         *            the rnr see
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        @Deprecated
        public Voice(String user, String pass, String source, String rnrSee)
                        throws IOException {

                this.user = user;
                this.pass = pass;
                this.rnrSEE = rnrSee;
                this.source = source;

                login();
        }

        /**
         * A constructor which which allows a custom source.
         * This Constructor enables verbose output.
         * 
         * @param user
         *            the username in the format of user@gmail.com or user@googlemail.com
         * @param pass
         *            the password
         * @param source
         *            Short string identifying your application, for logging purposes. This string should take the form:
                                        "companyName-applicationName-versionID". See: http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Request
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public Voice(String user, String pass, String source) throws IOException {
                init(user, pass, source, true, GOOGLE, null, null);

        }

        /**
         * Instantiates a new Voice Object. This is generally the simplest and
         * preferred constructor. This Constructor enables verbose output.
         * 
         * @param user
         *            the username in the format of user@gmail.com or user@googlemail.com
         * @param pass
         *            the pass
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public Voice(String user, String pass) throws IOException {
                init(user, pass, null, true, GOOGLE, null, null);
        }

        /**
         * Instantiates a new voice. Custom Source Variable allowed, and
         * printDebugIntoSystemOut which allows for Verbose output.
         * 
         * @param user
         *            the username in the format of user@gmail.com or user@googlemail.com
         * @param pass
         *            the password
         * @param source
         *            the arbitrary source identifier.  Can be anything.
         * @param printDebugIntoToSystemOut
         *            the print debug into to system out
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public Voice(String user, String pass, String source,
                        boolean printDebugIntoToSystemOut) throws IOException {
                init(user, pass, source, printDebugIntoToSystemOut, GOOGLE, null, null);
        }
        
        /**
         * Instantiates a new voice. Custom Source Variable allowed, and
         * printDebugIntoSystemOut which allows for Verbose output.
         * 
         * @param user
         *            the username in the format of user@gmail.com or user@googlemail.com
         * @param pass
         *            the password
         * @param source
         *            the arbitrary source identifier.  Can be anything.
         * @param printDebugIntoToSystemOut
         *            the print debug into to system out
         * @param accountType
         *                        Type of account to request authorization for. Possible values are:
         *                      Voice.GOOGLE (get authorization for a Google account only) 
         *                      Voice.HOSTED (get authorization for a hosted account only) 
         *                      Voice.HOSTED_OR_GOOGLE (get authorization first for a hosted account; if attempt fails, get authorization for a Google account)
         *                      Use Voice.HOSTED_OR_GOOGLE if you're not sure which type of account you want authorization for. If the user information matches both a hosted and a Google account, only the hosted account is authorized.
         *
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public Voice(String user, String pass, String source,
                        boolean printDebugIntoToSystemOut, String accountType) throws IOException {
                init(user, pass, source, printDebugIntoToSystemOut, accountType, null, null);
        }
        
        /**
         * Instantiates a new voice. Custom Source Variable allowed, and
         * printDebugIntoSystemOut which allows for Verbose output.
         * 
         * @param user
         *            the username in the format of user@gmail.com or user@googlemail.com
         * @param pass
         *            the password
         * @param source
         *            the arbitrary source identifier.  Can be anything.
         * @param printDebugIntoToSystemOut
         *            the print debug into to system out
         * @param accountType
         *                        Type of account to request authorization for. Possible values are:
         *                      Voice.GOOGLE (get authorization for a Google account only) 
         *                      Voice.HOSTED (get authorization for a hosted account only) 
         *                      Voice.HOSTED_OR_GOOGLE (get authorization first for a hosted account; if attempt fails, get authorization for a Google account)
         *                      Use Voice.HOSTED_OR_GOOGLE if you're not sure which type of account you want authorization for. If the user information matches both a hosted and a Google account, only the hosted account is authorized.
         * @param captchaResponse
         *                              response to a captcha challenge, set to null if normal login
         * @param captchaToken
         *                              (optional) token which matches the response/url from the captcha challenge
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public Voice(String user, String pass, String source,
                        boolean printDebugIntoToSystemOut, String accountType, String captchaResponse, String captchaToken) throws IOException {
                init(user, pass, source, printDebugIntoToSystemOut, accountType, captchaResponse, captchaToken);
        }

        /**
         * Internal function used by all constructors to fully initiate the Voice
         * Object without chaptcha Response
         * 
         * @param user
         *            the username in the format of user@gmail.com or user@googlemail.com
         * @param pass
         *            the password for the google account
         * @param source
         *            the source
         * @param printDebugIntoToSystemOut
         *            the print debug into to system out
         * @param accountType
         *                        Type of account to request authorization for. Possible values are:
         *                      Voice.GOOGLE (get authorization for a Google account only) 
         *                      Voice.HOSTED (get authorization for a hosted account only) 
         *                      Voice.HOSTED_OR_GOOGLE (get authorization first for a hosted account; if attempt fails, get authorization for a Google account)
         *                      Use Voice.HOSTED_OR_GOOGLE if you're not sure which type of account you want authorization for. If the user information matches both a hosted and a Google account, only the hosted account is authorized.
         * @param captchaResponse
         *                              response to a captcha challenge, set to null if normal login
         * @param captchaToken
         *                              token which matches the response/url from the captcha challenge
         *
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        private void init(String user, String pass, String source,
                        boolean printDebugIntoToSystemOut, String accountType, String captchaResponse, String captchaToken) throws IOException {
                if(accountType==GOOGLE||accountType==HOSTED||accountType==HOSTED_OR_GOOGLE) {
                        this.account_type = accountType;
                        this.PRINT_TO_CONSOLE = printDebugIntoToSystemOut;
                        this.user = user;
                        this.pass = pass;
                        // this.rnrSEE = rnrSee;
                        if (source != null) {
                                this.source = source;
                        } else {
                                this.source = "GoogleVoiceJava";
                        }
                        
                        login(captchaResponse,captchaToken);
                        this.general = getGeneral();
                        setRNRSEE();
                } else {
                        throw new IOException("AccountType not valid");
                }
        }
        
        /**
         * Returns the username
         * @return username for gvoice account
         */
        public String getUsername()
        {
            return this.user;
        }
        

        
        /**
         * Returns the Group list - Lazy
         * @param refresh - set to true to force a List update from the server
         * @return List of Greeting objects
         * @throws IOException
         */
        public List<String> getGroupSettingsList(boolean forceUpdate) throws IOException {
//              return getSettings(forceUpdate).getGroupSettingsList();
//              List<String> lGList = new ArrayList<Group>();
//              String[] lGArray = getSettings(forceUpdate).getSettings().getGroups().;
//              for (int i = 0; i < lGArray.length; i++) {
//                      lGList.add(lGArray[i]);
//              }
//              return lGList;
                //TODO implement getGroupSettingsList
                return null;
        }
        
        /**
         * returns all users settings - lazy
         * @param forceUpdate
         * @return
         * @throws IOException 
         * @throws JSONException 
         */

        // public Voice(){
        // authToken = "abcde";
        // }

        /**
         * Fetches and returns the raw page source code for the Inbox.
         * 
         * @return the inbox
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getInbox() throws IOException {
                return get(inboxURLString);
        }
        
        public String getInboxPage(int page) throws IOException {
                return get(inboxURLString,page);
        }

        /**
         * Fetches the page Source Code for the Voice homepage. This file contains
         * most of the useful information for the Google Voice Account such as
         * attached PhoneOld info and Contacts.
         * 
         * @return the general
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getGeneral() throws IOException {
                return get(generalURLString);
        }
        
        /**
         * The main Google Voice section is paginated.  Access the raw HTML for 
         * specific page of the main section.
         *
         * @param page the page
         * @return the general page
         * @throws IOException Signals that an I/O exception has occurred.
         */
        public String getGeneralPage(int page) throws IOException {
                return get(generalURLString,page);
        }

        /**
         * Gets the raw page source code for the starred items.
         * 
         * @return the starred item page source
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getStarred() throws IOException {
                return get(starredURLString);
        }
        
        public String getStarredPage(int page) throws IOException {
                return get(starredURLString,page);
        }

        /**
         * Gets the raw page source code for the recent items.
         * 
         * @return the recent raw source code
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getRecent() throws IOException {
                return get(recentAllURLString);
        }
        
        public String getRecentPage(int page) throws IOException {
                return get(recentAllURLString,page);
        }

        /**
         * Gets the page source for the spam.
         * 
         * @return the spam
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getSpam() throws IOException {
                return get(spamURLString);
        }
        
        public String getSpamPage(int page) throws IOException {
                return get(spamURLString,page);
        }

        /**
         * Gets the page source for the recorded calls.
         * 
         * @return the recorded
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getRecorded() throws IOException {
                return get(recordedURLString);
        }
        
        public String getRecordedPage(int page) throws IOException {
                return get(recordedURLString,page);
        }

        /**
         * Gets the raw source code for the placed calls page.
         * 
         * @return the placed calls source code
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getPlaced() throws IOException {
                return get(placedURLString);
        }
        
        public String getPlacedPage(int page) throws IOException {
                return get(placedURLString,page);
        }

        /**
         * Gets the received calls source code.
         * 
         * @return the received
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getReceived() throws IOException {
                return get(receivedURLString);
        }
        
        public String getReceivedPage(int page) throws IOException {
                return get(receivedURLString,page);
        }

        /**
         * Gets the missed calls source code.
         * 
         * @return the missed
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getMissed() throws IOException {
                return get(missedURLString);
        }
        
        public String getMissedPage(int page) throws IOException {
                return get(missedURLString,page);
        }

  /**
         * Gets the Voicemail page raw source code.
         *
         * @return the Voicemail
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getVoicemail() throws IOException {
                return get(voicemailURLString);
        }

        public String getVoicemailPage(int page) throws IOException {
                return get(voicemailURLString,page);
        }

  /**
         * Downloads a voicemail
         *
   * @param id of the voicemail to download
         * @return byte output stream
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
  public ByteArrayOutputStream downloadVoicemail(String msgID) throws IOException
  {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    try
    {
      URL u = new URL (voicemailDownloadURLString + msgID);
      HttpURLConnection huc = (HttpURLConnection)u.openConnection () ;
      huc.setRequestProperty("Authorization", "GoogleLogin auth="+authToken);
      huc.setRequestProperty("User-agent", USER_AGENT);
      huc.setRequestMethod ("GET");
      huc.connect() ;
      InputStream is = huc.getInputStream();

      if(huc.getResponseCode() == HttpURLConnection.HTTP_OK)
      {
        byte[] buffer = new byte [4096];
        int bytes = 0;

        while(true)
        {
          bytes = is.read(buffer);
          if(bytes <= 0)
            break;
          outputStream.write(buffer, 0, bytes);
        }

        outputStream.flush();
      }
               
      huc.disconnect ();

      return outputStream;
    }
    catch(IOException e)
    {
      System.out.println ( "Exception\n" + e ) ;
    }

    return null;
  }

        /**
         * Gets the SMS page raw source code.
         * 
         * @return the sMS
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String getSMS() throws IOException {
                return get(smsURLString);
        }
        
        
        public String getSMSPage(int page) throws IOException {
                return get(smsURLString,page);
        }

        /**
         * Internal method which parses the Homepage source code to determine the
         * rnrsee variable, this variable is passed into most fuctions for placing
         * calls and sms.
         * @throws IOException 
         */
        private void setRNRSEE() throws IOException {
                if (general != null) {
                        if(general.contains("'_rnr_se': '")) {
                                String p1 = general.split("'_rnr_se': '", 2)[1];
                                rnrSEE = p1.split("',", 2)[0];
                                if(PRINT_TO_CONSOLE)
                                        System.out.println("Successfully Received rnr_se.");
                                p1 = null;
                        } else if(general.contains("<div class=\"gc-notice\">")) {
                                String gcNotice = removeUninterestingParts(general, "<div class=\"gc-notice\">", "</div>", false);  
                                System.out.println(gcNotice+ "(Answer did not contain rnr_se)");
                                throw new IOException(gcNotice + "(Answer did not contain rnr_se)");
                        } else {
                                System.out.println("Answer did not contain rnr_se! "+ general);
                                throw new IOException("Answer did not contain rnr_se! "+ general);
                        }
                } else {
                        System.out.println("setRNRSEE(): Answer was null!");
                        throw new IOException("setRNRSEE(): Answer was null!");
                }
        }

  public String getPhoneNumber()
  {
    return this.phoneNumber;
  }

        //TODO Combine with or replace setPhoneInfo
        public String getRawPhonesInfo() throws IOException{
                return get(phonesInfoURLString);
        }
        
        /**
         * Place a call.
         * 
         * @param originNumber
         *            the origin number
         * @param destinationNumber
         *            the destination number
         * @param phoneType
         *            the phone type, this is a number such as 1,2,7 formatted as a String
         * @return the raw response string received from Google Voice.
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String call(String originNumber, String destinationNumber,
                        String phoneType) throws IOException {
                String out = "";
                StringBuffer calldata = new StringBuffer();
                
                
                // POST /voice/call/connect/ 
                // outgoingNumber=[number to call]
                // &forwardingNumber=[forwarding number]
                // &subscriberNumber=undefined
                // &phoneType=[phone type from google]
                // &remember=0
                // &_rnr_se=[pull from page]
                
                calldata.append("outgoingNumber=");
                calldata.append(URLEncoder.encode(destinationNumber, enc));
                calldata.append("&forwardingNumber=");
                calldata.append(URLEncoder.encode(originNumber, enc));
                calldata.append("&subscriberNumber=undefined");
                calldata.append("&phoneType=");
                calldata.append(URLEncoder.encode(phoneType, enc));
                calldata.append("&remember=0");
                calldata.append("&_rnr_se=");
                calldata.append(URLEncoder.encode(rnrSEE, enc));
                
                
                URL callURL = new URL("https://www.google.com/voice/call/connect/");

                URLConnection callconn = callURL.openConnection();
                callconn.setRequestProperty("Authorization","GoogleLogin auth="+authToken);
                callconn.setRequestProperty("User-agent",USER_AGENT);

                callconn.setDoOutput(true);
                OutputStreamWriter callwr = new OutputStreamWriter(callconn
                                .getOutputStream());

                callwr.write(calldata.toString());
                callwr.flush();

                BufferedReader callrd = new BufferedReader(new InputStreamReader(
                                callconn.getInputStream()));

                String line;
                while ((line = callrd.readLine()) != null) {
                        out += line + "\n\r";

                }

                callwr.close();
                callrd.close();

                if (out.equals("")) {
                        throw new IOException("No Response Data Received.");
                }

                return out;

        }

        /**
         * Cancel a call that was just placed.
         * 
         * @param originNumber
         *            the origin number
         * @param destinationNumber
         *            the destination number
         * @param phoneType
         *            the phone type
         * @return the string
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String cancelCall(String originNumber, String destinationNumber,
                        String phoneType) throws IOException {
                String out = "";
                String calldata = "";
                calldata += URLEncoder.encode("outgoingNumber", enc) + "="
                                + URLEncoder.encode("undefined", enc);
                calldata += "&" + URLEncoder.encode("forwardingNumber", enc) + "="
                                + URLEncoder.encode("undefined", enc);

                calldata += "&" + URLEncoder.encode("cancelType", enc) + "="
                                + URLEncoder.encode("C2C", enc);
                calldata += "&" + URLEncoder.encode("_rnr_se", enc) + "="
                                + URLEncoder.encode(rnrSEE, enc);
                // POST /voice/call/connect/ outgoingNumber=[number to
                // call]&forwardingNumber=[forwarding
                // number]&subscriberNumber=undefined&remember=0&_rnr_se=[pull from
                // page]
                URL callURL = new URL("https://www.google.com/voice/call/cancel/");

                URLConnection callconn = callURL.openConnection();
                callconn.setRequestProperty( "Authorization",
                "GoogleLogin auth="+authToken );
                callconn
                                .setRequestProperty(
                                                "User-agent",
                                                USER_AGENT);

                callconn.setDoOutput(true);
                OutputStreamWriter callwr = new OutputStreamWriter(callconn
                                .getOutputStream());
                callwr.write(calldata);
                callwr.flush();

                BufferedReader callrd = new BufferedReader(new InputStreamReader(
                                callconn.getInputStream()));

                String line;
                while ((line = callrd.readLine()) != null) {
                        out += line + "\n\r";

                }

                callwr.close();
                callrd.close();

                if (out.equals("")) {
                        throw new IOException("No Response Data Received.");
                }

                return out;

        }
        
        public String deleteMessage(String msgID) throws IOException
  {
    String out = "";
                StringBuffer calldata = new StringBuffer();


                // POST /voice/inbox/deleteMessages/
                // messages=[messageID]
                // &trash=1
                // &_rnr_se=[pull from page]

                calldata.append("messages=");
                calldata.append(URLEncoder.encode(msgID, enc));
                calldata.append("&trash=1");
                calldata.append("&_rnr_se=");
                calldata.append(URLEncoder.encode(rnrSEE, enc));


                URL callURL = new URL("https://www.google.com/voice/inbox/deleteMessages/");

                URLConnection callconn = callURL.openConnection();
                callconn.setRequestProperty("Authorization","GoogleLogin auth="+authToken);
                callconn.setRequestProperty("User-agent",USER_AGENT);

                callconn.setDoOutput(true);
                OutputStreamWriter callwr = new OutputStreamWriter(callconn
                                .getOutputStream());

                callwr.write(calldata.toString());
                callwr.flush();

                BufferedReader callrd = new BufferedReader(new InputStreamReader(
                                callconn.getInputStream()));

                String line;
                while ((line = callrd.readLine()) != null) {
                        out += line + "\n\r";

                }

                callwr.close();
                callrd.close();

                if (out.equals("")) {
                        throw new IOException("No Response Data Received.");
                }

                return out;
  }
        
        /** 
         * Enables multiple phones in one post 
         *  
         * TODO Test this with multiple phones in an account
         *              Best would be to be able to construct a url which can switch multiple phones at a time
         * 
         * @param IDs Array of Phones to enable
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public void phonesEnable(int[] IDs) throws IOException {

                if(IDs.length<1) {
                        return;
                } else if(IDs.length==1) {
                        //launch single (no thread overhead)    
                        phoneEnable(IDs[0]);
                } else {
                        for (int i = 0; i < IDs.length; i++) {
                                //TODO spawn threads!
                                int j = IDs[i];
                                String paraString = URLEncoder.encode("enabled", enc) + "="
                                                + URLEncoder.encode("1", enc);
                                paraString += "&" + URLEncoder.encode("phoneId", enc) + "="
                                                + URLEncoder.encode(Integer.toString(j), enc);
                                paraString += "&" + URLEncoder.encode("_rnr_se", enc) + "="
                                        + URLEncoder.encode(rnrSEE, enc);
                        
                                phonesEnableDisableApply(paraString);
                        }
                }
                
        }
        
        /**
         * Enables one of the the phones attached to the account from ringing.
         * Requires the internal ID for that phone, as an integer, usually 1,2,3,
         * etc.
         * 
         * @param ID
         *            the iD
         * @return the raw response of the enable action.
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String phoneEnable(int ID) throws IOException {
                String paraString = URLEncoder.encode("enabled", enc) + "="
                                + URLEncoder.encode("1", enc);
                paraString += "&" + URLEncoder.encode("phoneId", enc) + "="
                                + URLEncoder.encode(Integer.toString(ID), enc);
                paraString += "&" + URLEncoder.encode("_rnr_se", enc) + "="
                                + URLEncoder.encode(rnrSEE, enc);
                return phonesEnableDisableApply(paraString);
        }
        
        /** 
         * Disables multiple phones in one post
         * 
         * TODO Test this with multiple phones in an account
         *              Make faster - spawn threads
         *      Best would be to be able to construct a url which can switch multiple phones at a time
         * 
         * @param IDs Array of Phones to disable
         * @return the raw response of the disable action.
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public void phonesDisable(int[] IDs) throws IOException {
                
                if(IDs.length<1) {
                        return;
                } else if(IDs.length==1) {
                        //launch single (no thread overhead)    
                        phoneDisable(IDs[0]);
                } else {
                        for (int i = 0; i < IDs.length; i++) {
                                //TODO spawn threads!
                                int j = IDs[i];
                                String paraString = URLEncoder.encode("enabled", enc) + "="
                                                + URLEncoder.encode("0", enc);
                                paraString += "&" + URLEncoder.encode("phoneId", enc) + "="
                                                + URLEncoder.encode(Integer.toString(j), enc);
                                paraString += "&" + URLEncoder.encode("_rnr_se", enc) + "="
                                        + URLEncoder.encode(rnrSEE, enc);
                        
                                phonesEnableDisableApply(paraString);
                        }
                }

        }

        /**
         * Disable one of the the phones attached to the account from ringing.
         * Requires the internal ID for that phone, as an integer, usually 1,2,3,
         * etc.
         * 
         * @param ID
         *            the iD
         * @return the raw response of the disable action.
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String phoneDisable(int ID) throws IOException {
                String paraString = URLEncoder.encode("enabled", enc) + "="
                                + URLEncoder.encode("0", enc);
                paraString += "&" + URLEncoder.encode("phoneId", enc) + "="
                                + URLEncoder.encode(Integer.toString(ID), enc);
                paraString += "&" + URLEncoder.encode("_rnr_se", enc) + "="
                                + URLEncoder.encode(rnrSEE, enc);
                return phonesEnableDisableApply(paraString);
        }

        /**
         * Executes the enable/disable action with the provided url params
         * 
         * @param paraString
         *            the URL Parameters (encoded), ie ?auth=3248sdf7234&enable=0&phoneId=1&enable=1&phoneId=2&_rnr_se=734682ghdsf
         * @return the raw response of the disable action.
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        private String phonesEnableDisableApply(String paraString) throws IOException {
                String out = "";

                
                // POST /voice/call/connect/ outgoingNumber=[number to
                // call]&forwardingNumber=[forwarding
                // number]&subscriberNumber=undefined&remember=0&_rnr_se=[pull from
                // page]

                //
                URL requestURL = new URL(phoneEnableURLString);

                URLConnection conn = requestURL.openConnection();
                conn.setRequestProperty( "Authorization",
                "GoogleLogin auth="+authToken );
                conn
                                .setRequestProperty(
                                                "User-agent",
                                                USER_AGENT);

                conn.setDoOutput(true);
                conn.setDoInput(true);

                OutputStreamWriter callwr = new OutputStreamWriter(conn
                                .getOutputStream());
                callwr.write(paraString);
                callwr.flush();

                BufferedReader callrd = new BufferedReader(new InputStreamReader(
                                conn.getInputStream()));

                String line;
                while ((line = callrd.readLine()) != null) {
                        out += line + "\n\r";

                }

                callwr.close();
                callrd.close();

                if (out.equals("")) {
                        throw new IOException("No Response Data Received.");
                }

                return out;

        }
        
        /**
         * Enables/disables the call Announcement setting (general for all phones)
         * 
         * @param announceCaller <br/>
         *            true Announces caller's name and gives answering options <br/>
         *            false Directly connects calls when phones are answered
         * @return the raw response of the disable action.
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String setCallPresentation(boolean announceCaller) throws IOException {
                String out = "";

                URL requestURL = new URL(generalSettingsURLString);
                /** 0 for enable, 1 for disable **/
                String announceCallerStr="";

                if(announceCaller) {
                        announceCallerStr = "0";
                        if (PRINT_TO_CONSOLE) System.out.println("Turning caller announcement on.");
                }
                else {
                        announceCallerStr = "1";
                        if (PRINT_TO_CONSOLE) System.out.println("Turning caller announcement off.");
                }
                
                String paraString = "";
                paraString += URLEncoder.encode("directConnect", enc) + "="
                                + URLEncoder.encode(announceCallerStr, enc);
                paraString += "&" + URLEncoder.encode("_rnr_se", enc) + "="
                                + URLEncoder.encode(rnrSEE, enc);


                URLConnection conn = requestURL.openConnection();
                conn.setRequestProperty( "Authorization",
                "GoogleLogin auth="+authToken );
                conn.setRequestProperty("User-agent",
                                                                USER_AGENT);

                conn.setDoOutput(true);
                conn.setDoInput(true);

                OutputStreamWriter callwr = new OutputStreamWriter(conn.getOutputStream());
                callwr.write(paraString);
                callwr.flush();

                BufferedReader callrd = new BufferedReader(new InputStreamReader(conn.getInputStream()));

                String line;
                while ((line = callrd.readLine()) != null) {
                        out += line + "\n\r";
                }

                callwr.close();
                callrd.close();

                if (out.equals("")) {
                        throw new IOException("No Response Data Received.");
                }

                return out;
        }
        
        /**
         * This is the general voicemail greeting callers hear
         * 
         * @param greetingToSet <br/>
         *            number of the greeting to choose
         * @return the raw response of the disable action.
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String setVoicemailGreetingId(String greetingToSet) throws IOException {

                URL requestURL = new URL(generalSettingsURLString);

                if (PRINT_TO_CONSOLE) System.out.println("Activating Greeting#"+greetingToSet);

                String paraString = "";
                // URLEncoder.encode("auth", enc) + "="+ URLEncoder.encode(authToken, enc);
                paraString += URLEncoder.encode("greetingId", enc) + "="
                                + URLEncoder.encode(greetingToSet+"", enc);
                paraString += "&" + URLEncoder.encode("_rnr_se", enc) + "="
                                + URLEncoder.encode(rnrSEE, enc);


                return postSettings(requestURL, paraString);
        }
        
        /**
         * Activated or deactivated the Do Not disturb function.<br>
         * Enable this to send to voicemail all calls made to your Google number.
         * @param dndEnabled true to enable dnd, false to disable it
         * @return
         * @throws IOException
         */
        public String setDoNotDisturb(boolean dndEnabled) throws IOException {

                URL requestURL = new URL(generalSettingsURLString);
                
                String enabled;

                if(dndEnabled) {
                        if (PRINT_TO_CONSOLE) System.out.println("Enabling dnd");
                        enabled = "1";
                } else {
                        if (PRINT_TO_CONSOLE) System.out.println("Disabling dnd");
                        enabled = "0";
                }

                String paraString = "";
                        // URLEncoder.encode("auth", enc) + "="+ URLEncoder.encode(authToken, enc);
                paraString += URLEncoder.encode("doNotDisturb", enc) + "="
                                + URLEncoder.encode(enabled+"", enc);
                paraString += "&" + URLEncoder.encode("_rnr_se", enc) + "="
                                + URLEncoder.encode(rnrSEE, enc);


                return postSettings(requestURL, paraString);
        }
        
        /**
         * Posts a settings change
         * 
         * @param requestURL
         * @param paraString
         * @return
         * @throws IOException
         */
        private String postSettings(URL requestURL, String paraString)
                        throws IOException {
                String out = "";
                HttpURLConnection conn = (HttpURLConnection) requestURL.openConnection();
                conn.setRequestProperty( "Authorization",
                "GoogleLogin auth="+authToken );
                conn.setRequestProperty("User-agent",
                                                                USER_AGENT);

                conn.setDoOutput(true);
                conn.setDoInput(true);

                OutputStreamWriter callwr = new OutputStreamWriter(conn.getOutputStream());
                callwr.write(paraString);
                callwr.flush();

                // Get the response
                conn.connect();
                int responseCode = conn.getResponseCode();
                if(PRINT_TO_CONSOLE)
                        System.out.println(requestURL + " - " + conn.getResponseMessage());
                InputStream is;
                if(responseCode==200) {
                        is = conn.getInputStream();
                } else {
                        is = conn.getErrorStream();
                }
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader callrd = new BufferedReader(isr);
                
                String line;
                while ((line = callrd.readLine()) != null) {
                        out += line + "\n\r";
                }

                callwr.close();
                callrd.close();

                if (out.equals("")) {
                        throw new IOException("No Response Data Received.");
                }
                
                if(PRINT_TO_CONSOLE) System.out.println(out);

                return out;
        }

        /**
         * Send an SMS
         * 
         * @param destinationNumber
         *            the destination number
         * @param txt
         *            the Text of the message. Messages longer than the allowed
         *            character length will be split into multiple messages.
         * @return the string
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public String sendSMS(String destinationNumber, String txt)
                        throws IOException {
                String out = "";
                String smsdata = "";

                smsdata += URLEncoder.encode("phoneNumber", enc) + "="
                                + URLEncoder.encode(destinationNumber, enc);
                smsdata += "&" + URLEncoder.encode("text", enc) + "="
                                + URLEncoder.encode(txt, enc);
                smsdata += "&" + URLEncoder.encode("_rnr_se", enc) + "="
                                + URLEncoder.encode(rnrSEE, enc);
                URL smsurl = new URL("https://www.google.com/voice/sms/send/");

                URLConnection smsconn = smsurl.openConnection();
                smsconn.setRequestProperty( "Authorization",
                "GoogleLogin auth="+authToken );
                smsconn
                                .setRequestProperty(
                                                "User-agent",
                                                USER_AGENT);

                smsconn.setDoOutput(true);
                OutputStreamWriter callwr = new OutputStreamWriter(smsconn.getOutputStream());
                callwr.write(smsdata);
                callwr.flush();

                BufferedReader callrd = new BufferedReader(new InputStreamReader(
                                smsconn.getInputStream()));

                String line;
                while ((line = callrd.readLine()) != null) {
                        out += line + "\n\r";

                }

                callwr.close();
                callrd.close();

                if (out.equals("")) {
                        throw new IOException("No Response Data Received.");
                }

                return out;
        }
        
        /*
         * TODO: REMOVE before release
         */
        public String getONLYFORTEST(String urlString) throws IOException {
                return get(urlString);
        }

        /**
         * HTTP GET request for a given URL String.
         * 
         * @param urlString
         *            the url string
         * @return the string
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        String get(String urlString) throws IOException {
                URL url = new URL(urlString);
                //+ "?auth=" + URLEncoder.encode(authToken, enc));

                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestProperty( "Authorization",
                "GoogleLogin auth="+authToken );
                conn.setRequestProperty(
                                                "User-agent",
                                                USER_AGENT);
                conn.setInstanceFollowRedirects(false); // will follow redirects of same protocol http to http, but does not follow from http to https for example if set to true

                // Get the response
                conn.connect();
                int responseCode = conn.getResponseCode();
                if(PRINT_TO_CONSOLE)
                        System.out.println(urlString + " - " + conn.getResponseMessage());
                InputStream is;
                if(responseCode==200) {
                        is = conn.getInputStream();
                } else if(responseCode==HttpURLConnection.HTTP_MOVED_PERM || responseCode==HttpURLConnection.HTTP_MOVED_TEMP || responseCode==HttpURLConnection.HTTP_SEE_OTHER || responseCode==307) {
                        redirectCounter++;
                        if(redirectCounter > MAX_REDIRECTS) {
                                redirectCounter = 0;
                                throw new IOException(urlString + " : " + conn.getResponseMessage() + "("+responseCode+") : Too manny redirects. exiting.");
                        }
                        String location = conn.getHeaderField("Location");
                        if(location!=null && !location.equals("")) {
                                System.out.println(urlString + " - " + responseCode + " - new URL: " + location);
                                return get(location);
                        } else {
                                throw new IOException(urlString + " : " + conn.getResponseMessage() + "("+responseCode+") : Received moved answer but no Location. exiting.");
                        }
                } else {
                        is = conn.getErrorStream();
                }
                redirectCounter = 0;
                
                if(is==null) {
                        throw new IOException(urlString + " : " + conn.getResponseMessage() + "("+responseCode+") : InputStream was null : exiting.");
                }
                
                String result="";
                try {
                        // Get the response
                        BufferedReader rd = new BufferedReader(new InputStreamReader(is));
                        
                        StringBuffer sb = new StringBuffer();
                        String line;
                        while ((line = rd.readLine()) != null) {
                                sb.append(line + "\n\r");
                        }
                        rd.close();
                        result = sb.toString();
                } catch (Exception e) {
                        throw new IOException(urlString + " - " + conn.getResponseMessage() + "("+responseCode+") - " +e.getLocalizedMessage());
                }
                return result;
        }
        
        
        /**
         * HTTP GET request for a given URL String and a given page number
         * 
         * 
         * @param urlString the url string
         * @param page number must be a natural number
         * @return the string
         * @throws IOException Signals that an I/O exception has occurred.
         */
        String get(String urlString,int page) throws IOException {
                URL url = new URL(urlString + "?page=p"+page);
                //url+="&page="+page;
                URLConnection conn = url.openConnection();
                conn.setRequestProperty( "Authorization",
                "GoogleLogin auth="+authToken );
                conn
                                .setRequestProperty(
                                                "User-agent",
                                                USER_AGENT);

                // Get the response
                BufferedReader rd = new BufferedReader(new InputStreamReader(conn
                                .getInputStream()));
                StringBuffer sb = new StringBuffer();
                String line;
                while ((line = rd.readLine()) != null) {
                        sb.append(line + "\n\r");
                }
                rd.close();
                String result = sb.toString();

                return result;
        }
        
        /**
         * Login Method to refresh authentication with Google Voice.
         * 
         * @throws IOException
         *             Signals that an I/O exception has occurred.
         */
        public void login()  throws IOException {
                login(null,null);
        }
        
        /**
         * Use this login method to login - use captchaAnswer to answer a captcha challenge
         * @param captchaAnswer
         *                              (optional) String entered by the user as an answer to a CAPTCHA challenge. - null to make a normal login attempt
         * @param captchaToken
         *                              (optional) token which matches the response/url from the captcha challenge
         * @throws IOException
         */
        public void login(String pCaptchaAnswer, String pCaptchaToken) throws IOException {

                String data = URLEncoder.encode("accountType", enc) + "="
                                + URLEncoder.encode(account_type, enc);
                data += "&" + URLEncoder.encode("Email", enc) + "="
                                + URLEncoder.encode(user, enc);
                data += "&" + URLEncoder.encode("Passwd", enc) + "="
                                + URLEncoder.encode(pass, enc);
                data += "&" + URLEncoder.encode("service", enc) + "="
                                + URLEncoder.encode(SERVICE, enc);
                data += "&" + URLEncoder.encode("source", enc) + "="
                                + URLEncoder.encode(source, enc);
                if(pCaptchaAnswer!=null && pCaptchaToken!=null) {
                        data += "&" + URLEncoder.encode("logintoken", enc) + "="
                                        + URLEncoder.encode(pCaptchaToken, enc);
                        data += "&" + URLEncoder.encode("logincaptcha", enc) + "="
                                        + URLEncoder.encode(pCaptchaAnswer, enc);
                }

                // Send data
                URL url = new URL(loginURLString);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn
                .setRequestProperty(
                                "User-agent",
                                USER_AGENT);
                
                conn.setDoOutput(true);
                OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
                wr.write(data);
                wr.flush();

                // Get the response
                conn.connect();
                int responseCode = conn.getResponseCode();
                if(PRINT_TO_CONSOLE)
                        System.out.println(loginURLString + " - " + conn.getResponseMessage());
                InputStream is;
                if(responseCode==200) {
                        is = conn.getInputStream();
                } else {
                        is = conn.getErrorStream();
                }
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader rd = new BufferedReader(isr);
                String line;
                String completelineDebug="";
                
                /*
                 * A failure response contains an error code and a URL to an error page that can be displayed to the user. 
                 * If the error code is a CAPTCHA challenge, the response also includes a URL to a CAPTCHA image and a special 
                 * token. Your application should be able to solicit an answer from the user and then retry the login request. 
                 * To display the CAPTCHA image to the user, prefix the CaptchaUrl value with "http://www.google.com/accounts/", 
                 * for example: " http://www.google.com/accounts/Captcha?ctoken=HiteT4b0Bk5Xg18_AcVoP6-yFkHPibe7O9EqxeiI7lUSN".
                 */
                String lErrorString = "Unknown Connection Error."; // ex: Error=CaptchaRequired

                // String AuthToken = null;
                while ((line = rd.readLine()) != null) {
                        completelineDebug += line+"\n";
                        if (line.contains("Auth=")) {
                                this.authToken = line.split("=", 2)[1].trim();
                                if (PRINT_TO_CONSOLE){
                                        System.out.println("Logged in to Google - Auth token received");
                                }
                        } else if (line.contains("Error=")) {
                                lErrorString = line.split("=", 2)[1].trim();
                                //error = getErrorEnumByCode(lErrorString);
                                if (PRINT_TO_CONSOLE)
                                        System.out.println("Login error - "+lErrorString);
                                
                                
                        }
                        if (line.contains("CaptchaToken=")) {
                                captchaToken = line.split("=", 2)[1].trim();
                        } 
                        
                        if (line.contains("CaptchaUrl=")) {
                                captchaUrl = "http://www.google.com/accounts/" + line.split("=", 2)[1].trim();
                        }
                        if (line.contains("Url=")) {
                                captchaUrl2 = line.split("=", 2)[1].trim();
                        }
                        

                }
                wr.close();
                rd.close();

//              if (PRINT_TO_CONSOLE){
//                      System.out.println(completelineDebug);
//              }
                
                if (this.authToken == null) {
                        //AuthenticationException.throwProperException(null, captchaToken, captchaUrl);
                	throw new IOException();
                }
                
                String response = this.getRawPhonesInfo();
                int phoneIndex = response.indexOf("gc-user-number-value\">");
                this.phoneNumber = response.substring(phoneIndex + 22, phoneIndex + 36);
                this.phoneNumber = this.phoneNumber.replaceAll("[^a-zA-Z0-9]", "");
                if (this.phoneNumber.indexOf("+") == -1) {
                        this.phoneNumber = "+1" + this.phoneNumber;
                }
        }


        
        public String getCaptchaUrl() {
                return captchaUrl;
        }
        
        public String getCaptchaToken() {
                return captchaToken;
        }

        /**
         * Fires a Get request for Recent Items. If the Response requests login
         * authentication or if an exception is thrown, a false is returned,
         * otherwise if arbitrary text is contained for a logged in account, a true
         * is returned.
         * 
         *TODO Examine methodology.
         * 
         * @return true, if is logged in
         */
        public boolean isLoggedIn() {
                String res;
                try {
                        res = getRecent();
                } catch (IOException e) {
                        return false;
                }
                if (res
                                .contains("<meta name=\"description\" content=\"Google Voice gives you one number")
                                && res
                                                .contains("action=\"https://www.google.com/accounts/ServiceLoginAuth?service="+SERVICE+"\"")) {
                        return false;
                } else {
                        if (res.contains("Enter a new or existing contact name")
                                        || res.contains("<json><![CDATA[")) {
                                return true;
                        }
                }
                return false;
        }
        /**
         * Strips the text from the uninteresting parts before and after the interesting part.
         * The return includes borders if includeBorders == true - Returns null when Exception occures<br/><br/>
         * Example:<br/>
         * removeUninterestingParts("Hello Toby  , How are you today? Fine.", "How are", "?" , <b>true</b>)<br/>
         * Returns: "How are you today?"<br/>
         * <br/>
         * removeUninterestingParts("Hello Joseph, How are you today? Fine.", "How are", "?" , <b>false</b>)<br/>
         * Returns: " you today"
         * 
         * @param text
         * @param startBorder
         * @param endBorder
         * @param includeBorders
         * @return
         */
        public final String removeUninterestingParts(String text, String startBorder, String endBorder, boolean includeBorders) {
                String ret="";
                try {
                        if(text!=null&&startBorder!=null&&endBorder!=null&&(text.indexOf(startBorder)!=-1)&&(text.indexOf(endBorder)!=-1) ) {
                                
                                if(includeBorders) {
                                        text = text.substring(text.indexOf(startBorder));
                                        if(text!=null) {
                                                ret = text.substring(0,text.indexOf(endBorder)+endBorder.length());
                                        } else {
                                                ret = null;
                                        }
                                } else {
                                        text = text.substring(text.indexOf(startBorder)+startBorder.length());
                                        if(text!=null) {
                                                ret = text.substring(0,text.indexOf(endBorder));
                                        } else {
                                                ret = null;
                                        }
                                }
                        
                        } else {
                                ret = null;
                        }
                } catch (Exception e) {
                        System.out.println("Exception "+e.getMessage());
                        System.out.println("Begin:"+startBorder);
                        System.out.println("End:"+endBorder);
                        System.out.println("Text:"+text);
                        e.printStackTrace();
                        ret = null;
                }
                return ret;
        }
        
}